#include <cbm.h>
#include <conio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "DriveRoutines.h"
#include "DriveTests.h"
#include "global.h"
#include "globalInput.h"
#include "Screen.h"
#include "Timer.h"


void benchmarkDrive(const unsigned char driveIndex)
{
	unsigned char driveNumber = drives[driveIndex].drive;

	writeStatusBarf("%d - Creating 4k PRG file....", driveNumber);
	drives[driveIndex].driveScores[PRG_WRITE_4K] = createPrgFile(driveNumber, 0x1000);

	writeStatusBarf("%d - Reading 4k PRG file....", driveNumber);
	drives[driveIndex].driveScores[PRG_READ_4K] = readPrgFile(driveNumber, 0x1000);

	writeStatusBarf("%d - Deleting 4k PRG file....", driveNumber);
	deletePrgFile(driveNumber);

	writeStatusBarf("%d - Creating 8k PRG file....", driveNumber);
	drives[driveIndex].driveScores[PRG_WRITE_8K] = createPrgFile(driveNumber, 0x2000);

	writeStatusBarf("%d - Reading 8k PRG file....", driveNumber);
	drives[driveIndex].driveScores[PRG_READ_8K] = readPrgFile(driveNumber, 0x2000);

	writeStatusBarf("%d - Deleting 8k PRG file....", driveNumber);
	deletePrgFile(driveNumber);

	//writeStatusBarf("%d - Creating 12K PRG file....", driveNumber);
	//drives[driveIndex].driveScores[PRG_WRITE_12K] = createPrgFile(driveNumber, 0x3000);

	//writeStatusBarf("%d - Reading 12K PRG file....", driveNumber);
	//drives[driveIndex].driveScores[PRG_READ_12K] = readPrgFile(driveNumber, 0x3000);

	//writeStatusBarf("%d - Deleting 12K PRG file....", driveNumber);
	//deletePrgFile(driveNumber);

	writeStatusBarf("%d - Creating 400 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_WRITE_400] = createSeqFile(driveNumber, 400);
	
	writeStatusBarf("%d - Reading 400 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_READ_400] = readSeqFile(driveNumber, 400);
	
	writeStatusBarf("%d - Deleting 400 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_DELETE] = deleteSeqFile(driveNumber);

	writeStatusBarf("%d - Creating 40 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_WRITE_40] = createSeqFile(driveNumber, 40);
	
	writeStatusBarf("%d - Reading 40 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_READ_40] = readSeqFile(driveNumber, 40);
	
	writeStatusBarf("%d - Deleting 40 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_DELETE] = deleteSeqFile(driveNumber);

	writeStatusBarf("%d - Creating 4 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_WRITE_4] = createSeqFile(driveNumber, 4);
	
	writeStatusBarf("%d - Reading 4 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_READ_4] = readSeqFile(driveNumber, 4);
	
	writeStatusBarf("%d - Deleting 4 block SEQ file....", driveNumber);
	drives[driveIndex].driveScores[SEQ_DELETE] = deleteSeqFile(driveNumber);

	writeStatusBarf("%d - Creating 50 record REL file....", driveNumber);
	drives[driveIndex].driveScores[REL_WRITE] = createRelFile(driveNumber);

	writeStatusBarf("%d - Reading 50 record REL file....", driveNumber);
	drives[driveIndex].driveScores[REL_READ] = randomReadRelFile(driveNumber);

	writeStatusBarf("%d - Deleting 50 record REL file....", driveNumber);
	deleteRelFile(driveNumber);

}

static unsigned char bytes[0x2000];

tr_t* createPrgFile(unsigned char driveNumber, unsigned int size)
{
	static bool errorOccurred = false;
	unsigned int i = 0;
	unsigned long dividend = 0, divisor = 0;
	static signed int r;
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	setClock(0, 0, 0, 0);

	result->startTime = getTime();
	result->endTime = NULL;
	
	if((r = cbm_save("CBMMark.prg", driveNumber, bytes, size)) == 0)
	{
		result->endTime = getTime();
		result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

		dividend = (unsigned long)(size/256u)*100000ul;
		divisor  = result->elapsedTime->Time;

		if(divisor == 0) 
			divisor = 1000ul;

		result->score = (unsigned int)(dividend / divisor) ;
		//waitForEnterEscf("%llu / %llu = %u", dividend, divisor, result->score);
	}	
	else
	{
		waitForEnterEscf("r: %u  _oserror: %u", r, _oserror);
	}

	return result; 
}

tr_t* readPrgFile(unsigned char driveNumber, unsigned int size)
{
	static bool errorOccurred = false;
	unsigned int i = 0;
	unsigned long dividend = 0, divisor = 0;
	static signed int r;
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	setClock(0, 0, 0, 0);

	result->startTime = getTime();
	result->endTime = NULL;
	
	if((r = cbm_load("CBMMark.prg", driveNumber, bytes)) > 0)
	{
		result->endTime = getTime();
		result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

		dividend = (unsigned long)(size/256u)*100000ul;
		divisor  = result->elapsedTime->Time;

		if(divisor == 0) 
			divisor = 1000ul;

		result->score = (unsigned int)(dividend / divisor) ;
		//waitForEnterEscf("%llu / %llu = %u", dividend, divisor, result->score);
	}	
	else
	{
		waitForEnterEscf("r: %u  _oserror: %u", r, _oserror);
	}

	return result; 
}

tr_t* deletePrgFile(unsigned char driveNumber)
{
	return deleteFile(driveNumber, "CBMMark.prg");
}

tr_t* createSeqFile(unsigned char driveNumber, unsigned int length)
{ 
	static bool errorOccurred = false;
	unsigned int i = 0;
	unsigned long dividend = 0, divisor = 0;
	static signed int r;
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	static unsigned char bytes[254] =
	{
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0
	};

	setClock(0, 0, 0, 0);

	result->startTime = getTime();
	result->endTime = NULL;
		
	if((r = cbmOpen(3, driveNumber, 3, "CBMMark.seq,s,w", 15)) == 0)
	{
		for(; i<length; ++i)
		{
			r = cbm_write(3, bytes, 254);
			if(r < 0)
			{
				waitForEnterEscf("r: %u _oserror: %u", r, _oserror);
				errorOccurred = true;
				break;
			}
		}
		cbm_close(3);
		cbm_close(15);

		if(!errorOccurred)
		{
			result->endTime = getTime();
			result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

			result->score = ((unsigned long)length*100000ul) / (result->elapsedTime->Time) ;
		}
	}	
	else
	{
		waitForEnterEscf(buffer);
	}

	return result; 
}

tr_t* readSeqFile(unsigned char driveNumber, unsigned int length)
{ 
	static bool errorOccurred = false;
	unsigned int i = 0;
	static signed int r;
	static unsigned char bytes[254] =
	{
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0
	};
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	setClock(0, 0, 0, 0);

	result->startTime = getTime();
	result->endTime = NULL;
	
	if((r = cbmOpen(2, driveNumber, 2, "CBMMark.seq,s,r", 15)) == 0)
	{
		for(; i<length; ++i)
		{
			r = cbm_read(2, bytes, 254);
			if(r < 0)
			{
				waitForEnterEscf("r: %u _oserror: %u", r, _oserror);
				errorOccurred = true;
				break;
			}
		}
		cbm_close(2);
		cbm_close(15);

		if(!errorOccurred)
		{
			result->endTime = getTime();
			result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

			result->score = ((unsigned long)length*100000ul) / (result->elapsedTime->Time) ;
		}
	}	
	else
	{
		waitForEnterEscf(buffer);
	}

	return result; 
}

tr_t* deleteSeqFile(unsigned char driveNumber)
{ 
	return deleteFile(driveNumber, "CBMMark.seq");
}

tr_t* deleteFile(unsigned char driveNumber, const char* filename)
{
	static bool errorOccurred = false;
	unsigned int i = 0;
	static signed int r;
	static char bytes[19];
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	setClock(0, 0, 0, 0);

	result->startTime = getTime();
	result->endTime = NULL;
	
	sprintf(bytes, "s:%s", filename);

	if((r = sendCommand(driveNumber, bytes, buffer)) == 0)
	{
		result->endTime = getTime();
		result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

		result->score = 100000ul / (unsigned int)(result->elapsedTime->Time) ;
	}
	else
	{
		waitForEnterEscf("delete error: %s", buffer);
	}

	return result; 
}

tr_t* createRelFile(unsigned char driveNumber)
{ 
	bool aborted = false;
	unsigned int i = 0, r = 0;
	static char bytes[254] =
	{
		255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0
	};
	char command[41], localbuffer[41];
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	sprintf(command, "0:CBMMarks.rel,l,%c", 254);

	buffer[0] = '\0';

	result->startTime = getTime();
	if((r = cbmOpen(2, driveNumber, 2, command, 15)) == 0)
	{
		//waitForEnterEscf("%d - %s", r, buffer);
			
		if(buffer[0] != '0' || buffer[1] != '0')
		{
			waitForEnterEscf("%d - %s", r, buffer);
			aborted = true;
		}

		for(i = 0; i < 50; ++i)
		{
			sprintf(bytes,"record: %u", i);
			sprintf(command,"p000");
			command[1] = 96 + 2;
			command[2] = (unsigned char)i+1;
			command[3] = 0;
			cbm_write(15, command, 4u);	// Position the record.
			cbm_write(15, command, 4u);	// Position the record.
			
			localbuffer[0] = '\0';
			r = cbm_read(15, localbuffer, 40u);
			if((localbuffer[0] != '0' && localbuffer[0] !='5') || localbuffer[1] != '0')
			{
				aborted = true;
				break;
			}

			cbm_write(2, bytes, 254);
		}

		cbm_close(15);
		cbm_close(2);
		
		result->endTime = getTime();

		result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

		if(!aborted)
		{
			result->score = (5000000ul) / (result->elapsedTime->Time);
		}
		else
		{
			result->score = 0;
		}

	}
	else
	{
		waitForEnterEscf(buffer);
	}

	return result; 
}

tr_t* randomReadRelFile(unsigned char driveNumber)
{ 
	bool aborted = false;
	unsigned int i = 0, r = 0;
	static char bytes[254] =
	{
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0
	};
	char command[41], localbuffer[41];
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));
		
	sprintf(command, "0:CBMMarks.rel,l,%c", 254);

	buffer[0] = '\0';

	result->startTime = getTime();
	if((r = cbmOpen(2, driveNumber, 3, command, 15)) == 0)
	{
		//waitForEnterEscf("%d - %s", r, buffer);
			
		if(buffer[0] != '0' || buffer[1] != '0')
		{
			waitForEnterEscf("%d - %s", r, buffer);
			aborted = true;
		}

		for(i = 0; i < 50; ++i)
		{
			sprintf(command,"p000");
			command[1] = 96 + 3;
			command[2] = (unsigned char)i+1;
			command[3] = 0;
			//writeStatusBarf("%d - %s", i, command);
			cbm_write(15, command, 4u);	// Position the record.
			cbm_write(15, command, 4u);	// Position the record.
			
			localbuffer[0] = '\0';
			r = cbm_read(15, localbuffer, 40u);
			if((localbuffer[0] != '0') || localbuffer[1] != '0')
			{
				aborted = true;
				break;
			}

			if((r = cbm_read(2, bytes, 254)) == 0)
			{
				waitForEnterEscf("i:%d - No bytes read", i);
				aborted = true;
				break;
			}
			else if(r < 0)
			{
				waitForEnterEscf("r:%d _oserror:%d", r, _oserror);
				aborted = true;
				break;
			}
		}

		cbm_close(2);
		cbm_close(15);
			
		result->endTime = getTime();

		result->elapsedTime = getElapsedTime(result->startTime, result->endTime);

		if(!aborted)
		{
			result->score = (5000000ul) / (result->elapsedTime->Time);
		}
		else
		{
			result->score = 0;
		}

	}
	else
	{
		waitForEnterEscf(buffer);
	}

	return result; 
}

tr_t* deleteRelFile(unsigned char driveNumber)
{ 
	tr_t* result = (tr_t*)malloc(sizeof(tr_t));

	//result->startTime = getTime();
	//result->endTime = getTime();
	//result->elapsedTime = getElapsedTime(result->startTime, result->endTime);
	//result->elapsedTime->Time = 0ul;
	//result->elapsedTime->Milisecond = 0;
	//result->score = 0;

	deleteFile(driveNumber, "CBMMarks.rel");

	return result; 
}

void displayDrives(unsigned char current)
{
	unsigned char i = 0;
	static char driveMessage[40];

	textcolor(COLOR_GRAY1);
	cvlinexy(0, 5, (drives[9].drive > 0 ? 10 : 9));

	for(i = 0; i<(drives[9].drive > 0 ? 10 : 9); ++i)
	{
		if(drives[i].isSelected)
		{
			textcolor(COLOR_WHITE);
			revers(true);
			cputcxy(1, 5 + i, 0xfa);
		}
		else
		{
			textcolor(COLOR_GRAY2);
			revers(false);
			cputcxy(1, 5 + i, 0x20);
		}

		sprintf(driveMessage, "%02d %s", drives[i].drive, drives[i].message + 3);
		cputsxy(2, 5 + i, driveMessage);
	}

	textcolor(COLOR_YELLOW);
	cputcxy(0, 5 + current, '>');
}

void selectDrives()
{
	unsigned char i = 0, key = 0, currentPosition = 0;
	bool cont = true;
	static const char message[]  = { "Press SPACE to select, RETURN to finish" };
	saveScreen();

	writeStatusBarf("Select drives to test.");

	displayDrives(currentPosition);

	cputsxy(20 - (sizeof(message)/2), 20, message);

	while(cont)
	{
		key = cgetc();

		switch(key)
		{
		case CH_CURS_UP:
			if(currentPosition > 0)
			{
				textcolor(COLOR_GRAY1);
				cputcxy(0, 5 + currentPosition, CH_VLINE);
				--currentPosition;
				textcolor(COLOR_YELLOW);
				cputcxy(0, 5 + currentPosition, '>');
			}
			break;
		case CH_CURS_DOWN:
			if(currentPosition < (drives[9].drive > 0 ? 9 : 8))
			{
				textcolor(COLOR_GRAY1);
				cputcxy(0, 5 + currentPosition, CH_VLINE);
				++currentPosition;
				textcolor(COLOR_YELLOW);
				cputcxy(0, 5 + currentPosition, '>');
			}
			break;
		case ' ':
			if(strlen(drives[currentPosition].message) > 0)
			{
				drives[currentPosition].isSelected = !drives[currentPosition].isSelected;
				if(currentPosition < (drives[9].drive > 0 ? 9 : 8))
				{
					textcolor(COLOR_GRAY1);
					cputcxy(0, 5 + currentPosition, CH_VLINE);
					++currentPosition;
					textcolor(COLOR_YELLOW);
					cputcxy(0, 5 + currentPosition, '>');
				}
				displayDrives(currentPosition);
			}
			break;

		case CH_ENTER:
			cont = false;
			break;
		}
	}

	retrieveScreen();
	return;
}